/**
 * Contain the whole agents tree
 */
package mas;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;

import rinde.sim.core.graph.Point;
import rinde.sim.core.model.pdp.PDPModel;
import rinde.sim.core.model.road.RoadModel;

/**
 * @author IVAN
 *
 */
public class Company {

	//ATTRIBUTES
	public static int DEFAULT_MASTERS_NUMBER; //calculated dynamically
	public static int DEFAULT_SLAVES_NUMBER;
	protected List<Master> trucks; 
    final RandomGenerator rnd = new MersenneTwister(234);
    //relative to the map size, the min and max radius of the agents
    public static double minimumRadius = 0.05;
    public static double maximumRadius = 1;
    //vary this (across different experiments) from 0 to 100 to vary the radius from minimumRadius to maximumRadius
    public double radiusPercentage = App.communicationQualityPercentage;
    //the radius that will be given to all agents
    public double agentRadiusRelativeToMap = minimumRadius + (maximumRadius-minimumRadius)*radiusPercentage;
    //remember that this reliability of agents is multiplied with eachother to get the actual probability that the message arrives
    //so don't set it too low
    public static double minimumReliability = 0.35;
    public static double maximumReliability = 1;
    //vary this (across different experiments) from 0 to 100 to vary the radius from minimumRadius to maximumRadius
    public double reliabilityPercentage = App.communicationQualityPercentage;
    //the radius that will be given to all agents
    public double agentReliability = minimumReliability + (maximumReliability-minimumReliability)*reliabilityPercentage;
    
	/**
	 * 
	 */
	public Company() {
	}
	
	
	/**
	 * Generates Agents tree (masters / slaves) in memory
	 * @param agents_number
	 * @param masters_percentage
	 * @return List<Master>
	 */
	public void buildTree(int agents_number, double masters_percentage, double xOffset, double yOffset, double mapWidth, double mapHeight, RoadModel rm, PDPModel pm){
    	Master master;
    	
    	int i;
    	
    	Company.DEFAULT_MASTERS_NUMBER = (int) Math.floor(agents_number * masters_percentage);
    	Company.DEFAULT_SLAVES_NUMBER = (agents_number - Company.DEFAULT_MASTERS_NUMBER);
    	int freeSlaves = Company.DEFAULT_SLAVES_NUMBER;
    	int absoluteRadius = (int)(mapWidth * this.agentRadiusRelativeToMap);
    	List<Master> tree = new ArrayList<Master>(Company.DEFAULT_MASTERS_NUMBER);
    	
    	//Create whole master set
    	for(i=1;i<=Company.DEFAULT_MASTERS_NUMBER;i++){
    		master = new Master(absoluteRadius,agentReliability,i,rm,pm);
    		tree.add(master);
    	} 

    	//Assign slaves to masters until no free slaves left
    	int slaveCounter = 1;
    	while (freeSlaves > 0)
    		for(i=0;i<Company.DEFAULT_MASTERS_NUMBER;i++){
    			if(freeSlaves > 0){
    				Master currentMaster = tree.get(i);
	    			Slave slave = new Slave(absoluteRadius, agentReliability, Company.DEFAULT_MASTERS_NUMBER + slaveCounter, rm, pm, currentMaster);
	    			currentMaster.registerSlave(slave);
	    			freeSlaves--;
	    			slaveCounter++;
    			}
    		}
    	
    	this.fillWithZones(tree, xOffset, yOffset, mapWidth, mapHeight, rm);
    	this.trucks = tree;
    }
	
	private void fillWithZones(List<Master> tree, double xOffset, double yOffset, double mapWidth, double mapHeight, RoadModel rm){
		Map map = new Map(xOffset, yOffset, mapWidth,mapHeight, DEFAULT_MASTERS_NUMBER);
		List<Zone> zones = map.getZones();
		//assign zones to each master(group)
		int i=0;
		while (i < zones.size()) {
			Master currentMaster = tree.get(i);
			Zone currentZone = zones.get(i);
			currentMaster.setZone(currentZone);
			Point randomPosition = rm.getRandomPosition(rnd);
			double tolerance = 0.25;
			while(currentMaster.getZone().getRelativeDistanceFromCenter(randomPosition)>tolerance){
				randomPosition = rm.getRandomPosition(rnd);
				tolerance = tolerance * 1.005;
			}
			currentMaster.setLocation(randomPosition);
			currentMaster.initializeSlaveLocations();
			currentZone.setReferencePoint(randomPosition);
			i++;
		}
	}

	public List<Master> getTrucks() {
		return trucks;
	}

	public void setTrucks(List<Master> trucks) {
		this.trucks = trucks;
	}
	
}